
/**
\file
Definition of the rainbow::viewport_stack class.
\author Sean Tromans
*/

#include <sun_pre_compiled.hpp>
#include <rainbow/viewport/viewport_stack.hpp>
#include <rainbow/viewport/viewport.hpp>

#if !defined(SUN_DISABLE_LUA)
#include <sun/lua/lua.hpp>
#endif

namespace rainbow
{

	//
	//	Public Member Functions
	//

	/**
	Push a new viewport onto the stack to be drawn.
	\note
		Can safetly be called while drawing. Will result in the new vp being drawn before any current vp's.
	@param vp The new viewport to add.
	\author Sean Tromans
	*/
	void viewport_stack::push( viewport_ptr vp )
	{
		if ( vp == nullptr )
		{
			throw null_viewport( );
		}

		m_queue.push( vp );
	}

	/**
	Build all viewport render queues and then begin drawing.
	\note
		This will also clear the stack of any viewports.
	@param step_fraction How far between logic updates this frame is.
	\author Sean Tromans
	*/
	void viewport_stack::draw( float step_fraction )
	{
		while ( !m_queue.empty( ) )
		{
			viewport_ptr vp = m_queue.front( );
			m_queue.pop( );

			auto const stacked_iter = std::find( m_stack.begin( ), m_stack.end( ), vp );

			if ( stacked_iter != m_stack.end( ) )
			{
				//	Already built, but has a dependency which will be drawn earlier.
				//	So remove from current position and re-add at the "top" of the stack.
				m_stack.erase( stacked_iter );
			}
			else
			{
				//	Not built yet.
				vp->build_render_queue( step_fraction, *this );
			}

			m_stack.push_back( vp );
		}

		//	The viewports are added to this vector in stack order, so we need to iterate backwards to get queue order.
		std::for_each( m_stack.rbegin( ), m_stack.rend( ), [&step_fraction]( viewport_ptr vp )
		{
			vp->draw( step_fraction );
		} );

		//	Deconstructs all the shared_ptr's. Potentially slow. But the best place to do it while waiting for the GPU to work.
		m_stack.clear( );
	}

	//
	//	Static Member Functions.
	//

	/**
	Exposes the viewport_stack to lua.
	@param L The Lua State being initialised.
	\author Sean Tromans.
	*/
	void viewport_stack::lua_interface( lua_State * L )
	{
#if !defined(SUN_DISABLE_LUA)
		luabind::module( L, "rainbow" )
		[
			luabind::class_<viewport_stack>( "viewport_stack" )
				.def( "push", &viewport_stack::push )
				.def( "draw", &viewport_stack::draw )
		];
#else
		NMN_UNUSED( L );
#endif
	}

};
